/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * ConnectToServerDialog.java
 *
 * Created on 13.11.2009, 01:53:06
 */

package gui;

import data.HistoryCollector;
import data.Position;
import data.SensorData;
import exceptions.ClientAlreadyExistsException;
import exceptions.IllegalTagIdFormatException;
import java.awt.Component;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import listener.ConnectionListener;
import misc.ServerLogger;
import models.SimpleListModel;
import network.NormalizedServerDataReader;
import network.ServerDataReader;
import network.ServerReader;
import network.VelocityNormalizerDataReader;

/**
 *
 * @author tscheinecker
 */
public class ConnectToServerDialog extends javax.swing.JDialog {

    private SimpleListModel simpleListModel;
    private MoteTrackApp parent;
    private boolean connectionEstablished;
    private ServerDataReader client;
    private ConnectToServerDialog ctsd;
    private VelocityNormalizerDataReader obs;
    private SpinnerNumberModel maxVelocitySpinnerModel;

    /** Creates new form ConnectToServerDialog */
    public ConnectToServerDialog(MoteTrackApp parent, boolean modal) {
        super(parent, modal);
        normalizationPos = new Position(0, 0, 0);
        simpleListModel = new SimpleListModel();
        maxVelocitySpinnerModel = new SpinnerNumberModel(3.0, 0.0, 100.0, 0.1);
        this.parent = parent;
        initComponents();

        setResizable(true);
        maxVelocitySpinner.addChangeListener(maxValueSpinnerChangelistener);

        connectionEstablished = false;
        ctsd = this;
    }

    private ChangeListener maxValueSpinnerChangelistener = new ChangeListener() {

            public void stateChanged(ChangeEvent e) {
                parent.setMaxVelocitySpinnerValue(getMaxVelocity());
            }
     };

     public double getMaxVelocity() {
         return Double.parseDouble(maxVelocitySpinner.getValue().toString());
     }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        connectButton = new javax.swing.JButton();
        abortButton = new javax.swing.JButton();
        serverAddressTextField = new javax.swing.JTextField();
        replayLogLabel = new javax.swing.JLabel();
        tagIdListScrollPane = new javax.swing.JScrollPane();
        tagIdList = new javax.swing.JList();
        tagIdTextField = new javax.swing.JTextField();
        removeButton = new javax.swing.JButton();
        idLabel = new javax.swing.JLabel();
        addButton = new javax.swing.JButton();
        normalizeCheckBox = new javax.swing.JCheckBox();
        logCheckBox = new javax.swing.JCheckBox();
        pathTextField = new javax.swing.JTextField();
        pathLabel = new javax.swing.JLabel();
        filenameLabel = new javax.swing.JLabel();
        filenameTextField = new javax.swing.JTextField();
        browseButton = new javax.swing.JButton();
        maxVelocityCheckBox = new javax.swing.JCheckBox();
        maxVelocitySpinner = new javax.swing.JSpinner();
        maxVelocityLabel = new javax.swing.JLabel();
        serverComboBox = new javax.swing.JComboBox();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Start Server");
        setAlwaysOnTop(true);
        setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        setName("connectDialog"); // NOI18N
        setResizable(false);

        connectButton.setText("Connect");
        connectButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                connectButtonActionPerformed(evt);
            }
        });

        abortButton.setText("Abort");
        abortButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                abortButtonActionPerformed(evt);
            }
        });

        serverAddressTextField.setText("localhost:5000");

        replayLogLabel.setText("IP Address");

        tagIdList.setModel(simpleListModel);
        tagIdListScrollPane.setViewportView(tagIdList);

        removeButton.setText("-");
        removeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeButtonActionPerformed(evt);
            }
        });

        idLabel.setText("IDs");

        addButton.setText("+");
        addButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addButtonActionPerformed(evt);
            }
        });

        normalizeCheckBox.setText("normalize data");

        logCheckBox.setText("log data");
        logCheckBox.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                logCheckBoxStateChanged(evt);
            }
        });

        pathTextField.setText("../MoteTrack/logs/");
        pathTextField.setEnabled(false);

        pathLabel.setText("Log Path");
        pathLabel.setEnabled(false);

        filenameLabel.setText("Filename");
        filenameLabel.setEnabled(false);

        filenameTextField.setText("<yy'.'MM'.'dd kk'-'mm'-'ss>.log");
        filenameTextField.setEnabled(false);

        browseButton.setText("Browse");
        browseButton.setEnabled(false);
        browseButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                browseButtonActionPerformed(evt);
            }
        });

        maxVelocityCheckBox.setText("velocity observer");
        maxVelocityCheckBox.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                maxVelocityCheckBoxStateChanged(evt);
            }
        });

        maxVelocitySpinner.setModel(maxVelocitySpinnerModel);
        maxVelocitySpinner.setEnabled(false);
        maxVelocitySpinner.setValue(100);

        maxVelocityLabel.setText("m/s");
        maxVelocityLabel.setEnabled(false);

        serverComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "localhost:5000", "192.168.93.10:6666" }));
        serverComboBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                serverComboBoxItemStateChanged(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                                .addComponent(addButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 43, Short.MAX_VALUE)
                                .addComponent(removeButton))
                            .addComponent(tagIdTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 121, Short.MAX_VALUE)
                            .addComponent(replayLogLabel)
                            .addComponent(serverAddressTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 121, Short.MAX_VALUE)
                            .addComponent(serverComboBox, 0, 121, Short.MAX_VALUE)
                            .addComponent(tagIdListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 121, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGap(23, 23, 23))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(idLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(maxVelocityCheckBox)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(maxVelocitySpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(maxVelocityLabel)
                        .addGap(14, 14, 14))
                    .addComponent(normalizeCheckBox, javax.swing.GroupLayout.DEFAULT_SIZE, 225, Short.MAX_VALUE)
                    .addComponent(logCheckBox)
                    .addComponent(pathLabel)
                    .addComponent(filenameLabel)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                        .addComponent(filenameTextField, javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                            .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(browseButton))
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(connectButton)
                            .addGap(18, 18, 18)
                            .addComponent(abortButton)))))
        );

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {abortButton, connectButton});

        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(replayLogLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(normalizeCheckBox)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(maxVelocityCheckBox)
                            .addComponent(maxVelocitySpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(maxVelocityLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(logCheckBox)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(pathLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(browseButton))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(filenameLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(filenameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(serverAddressTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(5, 5, 5)
                        .addComponent(serverComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(idLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(tagIdListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
                .addComponent(tagIdTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(addButton)
                    .addComponent(connectButton)
                    .addComponent(abortButton)
                    .addComponent(removeButton))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void connectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectButtonActionPerformed
        String ids = simpleListModel.getListEntries().replaceAll("-", "");
        String server = serverAddressTextField.getText();
        if (server.length() == 0) {
            JOptionPane.showMessageDialog(this, "You must provide a server address", "Server Address Missing", JOptionPane.ERROR_MESSAGE);
            return;
        }
        String[] split = server.split(":");
        if (split.length != 2) {
            JOptionPane.showMessageDialog(this, "The server address format has to be:\n IP_Address:Port", "Invalid Server Address", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        String address = split[0];

        try {
            int port = Integer.parseInt(split[1]);
            ServerDataReader newClient = new ServerDataReader(address, port, ids);
            if (normalizeCheckBox.isSelected()) {
                Position pos = normalizationPos;
                NormalizedServerDataReader obs = new NormalizedServerDataReader(pos);
                newClient.addObserver(obs);
                parent.setCalibrationSpinnerValue(3);
            }

            if (maxVelocityCheckBox.isSelected()) {
                obs = new VelocityNormalizerDataReader(Double.valueOf(maxVelocitySpinner.getValue().toString()));
                newClient.addObserver(obs);
            }

            if (logCheckBox.isSelected()) {
                // Append path seperator at end of path name
                String path = pathTextField.getText();
                char lastChar = path.charAt(path.length()-1);
                if (lastChar != '/' && lastChar != '\\') {
                    path = path+File.separatorChar;
                }


                String filename = path + formatFilename(filenameTextField.getText());
                File file = new File(filename);
                ServerLogger logger;
                if (file.exists()) {
                    int answer = JOptionPane.showConfirmDialog(this, "File already exists - Override?", "File Already Exists", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
                    if (answer == JOptionPane.YES_OPTION) {
                        logger = new ServerLogger(filename);
                        newClient.addObserver(logger);
                    }
                } else {
                    logger = new ServerLogger(filename);
                    newClient.addObserver(logger);
                }
            }

            if (client != null) {
                int answer = showYesNoDialog("Already connected to a Server. Connection will be closed.\nProceed?", "Already Connected");
                if (answer == JOptionPane.NO_OPTION) {
                    return;
                }

                client.removeConnectionListener(connectionListener);
                client.stopReader();
            }

            newClient.addConnectionListener(connectionListener);
            newClient.startReader();
            client = newClient;
//            if (!connectionEstablished) {
//                return;
//            }
        } catch (ClientAlreadyExistsException e) {
            JOptionPane.showMessageDialog(this, "A client on this sever at this port already exists", "Cient Already Exists", JOptionPane.ERROR_MESSAGE);
            return;
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(this, "The Port has to be a number", "Invalid Port", JOptionPane.ERROR_MESSAGE);
            return;
        }
    }//GEN-LAST:event_connectButtonActionPerformed

    private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addButtonActionPerformed
        
        try {
            String tagId = SensorData.formatId(tagIdTextField.getText(), false);
            simpleListModel.addElement(tagId);
        } catch (IllegalTagIdFormatException e) {
            JOptionPane.showMessageDialog(this, "You entered an invalid Tag ID - "+e.getIllegalTagId(), "Invalid Tag ID", JOptionPane.ERROR_MESSAGE);
        }
        
    }//GEN-LAST:event_addButtonActionPerformed

    private void removeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeButtonActionPerformed
        simpleListModel.removeElements(tagIdList.getSelectedIndices());
    }//GEN-LAST:event_removeButtonActionPerformed

    private void abortButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_abortButtonActionPerformed
        dispose();
    }//GEN-LAST:event_abortButtonActionPerformed

    private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
        JFileChooser fc = new JFileChooser();
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        String workdir = System.getProperty("user.dir");
        fc.setSelectedFile(new File(workdir));
        int value = fc.showOpenDialog(this);
        if (value == JFileChooser.APPROVE_OPTION) {
            File dir = fc.getSelectedFile();
            pathTextField.setText(dir.getPath());
        }
    }//GEN-LAST:event_browseButtonActionPerformed

    private void logCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_logCheckBoxStateChanged
        boolean on = logCheckBox.isSelected();
        browseButton.setEnabled(on);
        pathLabel.setEnabled(on);
        pathTextField.setEnabled(on);
        filenameLabel.setEnabled(on);
        filenameTextField.setEnabled(on);
    }//GEN-LAST:event_logCheckBoxStateChanged

    private void maxVelocityCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_maxVelocityCheckBoxStateChanged
        boolean selected = maxVelocityCheckBox.isSelected();
        maxVelocitySpinner.setEnabled(selected);
        parent.setMaxVelocitySpinnerEnabled(selected);
        maxVelocityLabel.setEnabled(selected);
    }//GEN-LAST:event_maxVelocityCheckBoxStateChanged

    private void serverComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_serverComboBoxItemStateChanged
        serverAddressTextField.setText(serverComboBox.getModel().getSelectedItem().toString());
    }//GEN-LAST:event_serverComboBoxItemStateChanged

    private SimpleDateFormat dateFormat;

    private String formatFilename(String filename) {
        // Remove Path Seperator at beginning of filename
        char firstChar = filename.charAt(0);
        if (firstChar == '/' || firstChar == '\\') {
            filename = filename.replaceFirst(""+firstChar, "");
        }

        Date date = new Date();
        dateFormat = new SimpleDateFormat();
        int sml = filename.indexOf('<'), grt = filename.indexOf('>');
        while (sml != -1 && grt != -1){
            String parsePattern = filename.substring(sml+1, grt);
            dateFormat.applyPattern(parsePattern);
            String formatted = dateFormat.format(date);
            filename = filename.replaceAll("<"+parsePattern+">", formatted);
            sml = filename.indexOf('<');
            grt = filename.indexOf('>');
        }

        return filename;
    }

    private void showErrorDialog(String msg, String title) {
        showErrorDialog(this, msg, title);
    }

    private void showErrorDialog(Component comp, String msg, String title) {
        JOptionPane.showMessageDialog(comp, msg, title, JOptionPane.ERROR_MESSAGE);
    }

    private void showInformationDialog(String msg, String title) {
        showInformationDialog(this, msg, title);
    }

    private void showInformationDialog(Component comp, String msg, String title) {
        JOptionPane.showMessageDialog(comp, msg, title, JOptionPane.INFORMATION_MESSAGE);
    }

    private int showYesNoDialog(String msg, String title) {
        return JOptionPane.showConfirmDialog(this, msg, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
    }


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton abortButton;
    private javax.swing.JButton addButton;
    private javax.swing.JButton browseButton;
    private javax.swing.JButton connectButton;
    private javax.swing.JLabel filenameLabel;
    private javax.swing.JTextField filenameTextField;
    private javax.swing.JLabel idLabel;
    private javax.swing.JCheckBox logCheckBox;
    private javax.swing.JCheckBox maxVelocityCheckBox;
    private javax.swing.JLabel maxVelocityLabel;
    private javax.swing.JSpinner maxVelocitySpinner;
    private javax.swing.JCheckBox normalizeCheckBox;
    private javax.swing.JLabel pathLabel;
    private javax.swing.JTextField pathTextField;
    private javax.swing.JButton removeButton;
    private javax.swing.JLabel replayLogLabel;
    private javax.swing.JTextField serverAddressTextField;
    private javax.swing.JComboBox serverComboBox;
    private javax.swing.JList tagIdList;
    private javax.swing.JScrollPane tagIdListScrollPane;
    private javax.swing.JTextField tagIdTextField;
    // End of variables declaration//GEN-END:variables


    private ConnectionListener connectionListener = new ConnectionListener() {

        public void connectionStateChanged(ServerReader source, boolean connected, boolean intended) {
            connectionEstablished = connected;

            if (!intended) {
                showErrorDialog("Connection failed", "Connection Failed");
                client = null;
                return;
            }
            
            if (connected) {
                showInformationDialog("Connection to server established", "Connection Established");
                parent.connectToServer(client, new HistoryCollector(client), obs);
                ctsd.dispose();
            } else {
                showInformationDialog("Disconnected from server", "Server Disconnected");
                client = null;
            }
        }

        public void unknownHost(ServerReader source, String host) {
            showErrorDialog("Unknown host: "+host, "Unknown Host");
            client = null;
        }
    };

    public void setTagIdFilter(String[] ids) {
        for (String s : ids) {
            simpleListModel.addElement(s);
        }
    }

    private Position normalizationPos;

    public void setNormalizationPos(Position pos) {
        normalizationPos = pos;
    }

    public void setNormalizationSelected(boolean selected) {
        normalizeCheckBox.setSelected(selected);
    }

    public void setServerAddress(String server) {
        serverAddressTextField.setText(server);
    }

    public void setLogFile(String logFile) {
        filenameTextField.setText(logFile);
    }

    public void setLogPath(String logPath) {
        pathTextField.setText(logPath);
    }

    public void setLogSelected(boolean selected) {
        logCheckBox.setSelected(selected);
    }

    public void setMaxVelocity(int maxVelocity) {
        maxVelocitySpinner.setValue(maxVelocity);
    }

    public void setMaxVelocitySelected(boolean selected) {
        maxVelocityCheckBox.setSelected(selected);
    }
}
